package com.zhifei.system.util.ding;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class DingSalaryUtils {

    public static final String host = "https://salary.eapps.dingtalkcloud.com";
    public static final String appId = "8130749";
    public static final String appSecret = "TpBDhWjYPnjn4OcbKeiopkq4ou7hmQpc";

    public static void main(String[] args) throws Exception {
        // 获取该⽉薪资组浮动薪资项
        String uri = "/oapi/salary/bill/list";
        // 构建参数
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("billMonth", "2022-10");
        paramsMap.put("page", "1");
        // json序列化
        String paramsData = JSON.toJSONString(paramsMap);
        // 获取签名
        String serviceSign = serviceSign(uri, "POST", Maps.newHashMap(), paramsData.getBytes());
        // 请求地址
        String url = host + uri;
        // 发起请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("signature", serviceSign);
        httpPost.setHeader("app-id", appId);
        httpPost.setEntity(new ByteArrayEntity(paramsData.getBytes()));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        // 解析结构
        HttpEntity entity = response.getEntity();
        String responseContent = EntityUtils.toString(entity, "UTF-8");
        System.out.println(responseContent);
        response.close(); httpClient.close();
    }

    public static String serviceSign(String uri, String httpMethod, Map<String, Object> paramsMap, byte[] body) throws Exception {
        String hmacSHA256 = "HmacSHA256";
        String encoding = "UTF-8";
        String bodyMd5 = buildBodyMd5(httpMethod, body);
        String resourceToSign = buildResource(uri, paramsMap);
        String stringToSign = buildStringToSign(resourceToSign, httpMethod, bodyMd5);
        Mac hmacSha256 = Mac.getInstance(hmacSHA256);
        byte[] keyBytes = appSecret.getBytes(encoding);
        hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, hmacSHA256));
        return new String(Base64.encodeBase64(hmacSha256.doFinal(stringToSign.getBytes(encoding))), encoding);
    }

    public static String buildBodyMd5(String httpMethod, byte[] body) throws IOException {
        String methodPost = "post";
        String methodPut = "put";
        if (body == null) {
            return null;
        } if (!httpMethod.equalsIgnoreCase(methodPost) && !httpMethod.equalsIgnoreCase(methodPut)) {
            return null;
        }
        InputStream inputStream = new ByteArrayInputStream(body);
        byte[] bodyBytes = IOUtils.toByteArray(inputStream);
        if (bodyBytes != null && bodyBytes.length > 0) {
            return base64AndMD5(bodyBytes).trim();
        }
        return null;
    }

    public static String base64AndMD5(byte[] bytes) {
        try {
            final MessageDigest md = MessageDigest.getInstance("MD5");
            md.reset();
            md.update(bytes);
            final Base64 base64 = new Base64();
            return new String(base64.encode(md.digest()));
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("unknown algorithm MD5");
        }
    }

    public static String buildResource(String uri, Map<String, Object> paramsMap) {
        StringBuilder builder = new StringBuilder();
        builder.append(uri);
        TreeMap<String, Object> sortMap = new TreeMap<>();
        sortMap.putAll(paramsMap);
        if (sortMap.size() > 0) {
            builder.append('?');
            builder.append(buildMapToSign(sortMap));
        }
        return builder.toString();
    }

    public static String buildStringToSign(String resourceToSign, String method, String bodyMd5) {
        char LF = '\n';
        StringBuilder sb = new StringBuilder();
        sb.append(method).append(LF);
        if (StringUtils.isNotBlank(bodyMd5)) {
            sb.append(bodyMd5);
        }
        sb.append(LF);
        sb.append(resourceToSign);
        return sb.toString();
    }

    public static String buildMapToSign(Map<String, Object> paramMap) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> e : paramMap.entrySet()) {
            if (builder.length() > 0) {
                builder.append('&');
            }
            String key = e.getKey();
            Object value = e.getValue();
            if (value != null) {
                if (value instanceof List) {
                    List list = (List) value;
                    if (list.size() == 0) {
                        builder.append(key);
                    } else {
                        builder.append(key).append("=").append(String.valueOf(list.get(0)));
                    }
                } else if (value instanceof Object[]) {
                    Object[] objs = (Object[]) value;
                    if (objs.length == 0) {
                        builder.append(key);
                    } else {
                        builder.append(key).append("=").append(String.valueOf(objs[0]));
                    }
                } else {
                    builder.append(key).append("=").append(String.valueOf(value));
                }
            }
        }
        return builder.toString();
    }

}
